package serveur;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.sql.rowset.Predicate;

import remote.ClientInterface;
import remote.Tweet;
import serveur.exception.AlreadyExistException;
import serveur.exception.NotExistException;
import serveur.exception.WrongPasswordException;

public class ServerDatas implements Serializable {
	// Singleton
	private static ServerDatas instance;

	// Users data
	private static String serializeFile = "datas.ser";	
	
	// Users
	private Map<String, User> users;
	// Users's tweets
	private ArrayList<Tweet> tweets;
	
	transient public Map<String, ClientInterface> connected;

	private ServerDatas() {
		users = new HashMap<String, User>();
		tweets = new ArrayList<Tweet>();
		connected = new HashMap<String, ClientInterface>();
	}

	/**
	 * Get Server singleton
	 * 
	 * @return Server
	 */
	public static ServerDatas getInstance() {
		if (instance == null) {
			instance = ServerDatas.unserialize();
		}
		return instance;
	}
	
	/**
	 * Method called to save server's state
	 */
	public void serialize() {
		ObjectOutputStream oos;
		try {
			// serialization file
			File fichier = new File(serializeFile);
			// open stream
			oos = new ObjectOutputStream(new FileOutputStream(fichier));
			// serialize users
			oos.writeObject(this);
			// close stream
			oos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Get users datas from serializate file
	 */
	private static ServerDatas unserialize() {
		try {
			// serialization file
			File fichier = new File(serializeFile);
			// open stream
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
					fichier));
			// unserialize
			instance = (ServerDatas) ois.readObject();
			instance.connected = new HashMap<String, ClientInterface>();
			// close stream
			ois.close();
			return instance;
		} catch (ClassNotFoundException | IOException e) {
			return new ServerDatas();
		}
	}
	
	/**
	 * Add a new user in ddb
	 * @param pseudo Pseudo
	 * @param paswd Password
	 * @return User
	 * @throws AlreadyExistException Pseudo already exist
	 */
	public User addUser(String pseudo, String paswd) throws AlreadyExistException {
		if(users.containsKey(pseudo)) throw new AlreadyExistException();
		// create and add new user
		User u = new User(pseudo, paswd);
		users.put(pseudo, u);
		serialize();
		return u;
	}

	/**
	 * Get user in ddb
	 * @param pseudo Pseudo
	 * @param paswd Password
	 * @return User
	 * @throws NotExistException Pseudo doesn't exist
	 */
	public User getUser(String pseudo) throws NotExistException {
		User u = users.get(pseudo);
		if(u == null) throw new  NotExistException();  // not exist
		return u; // everything OK
	}
	
	/**
	 * Add a tweet
	 * @param t
	 */
	public void addTweet(Tweet t) {
		t.setDate(new Date());
		tweets.add(0, t);
	}

	/**
	 * Get the X recent tweets
	 * @param x Number of tweet
	 * @return
	 */
	public List<Tweet> getRecentTweets(int x) {
		if(x <= tweets.size())
			return new ArrayList<Tweet>(tweets.subList(0, x));
		else return tweets;
	}

	/**
	 * Get the X recent tweet with a specific #tag
	 * @param htag #Tag
	 * @param x Number of tweet
	 * @return
	 */
	public List<Tweet> getRecentTweets(String htag, int x) {
		List<Tweet> res = new LinkedList<>();
		Iterator<Tweet> it = tweets.iterator();
		while(it.hasNext()) {
			Tweet t = it.next();
			if(htag.equals(t.getHtag()))
				res.add(t);
		}
		if(x < res.size())
			return new ArrayList<Tweet>(res.subList(0, x));
		else return res;
	}
	
	/**
	 * Get tweet belongs to an user
	 * @param pseudo User's pseudo
	 * @return All the user's tweets
	 */
	public List<Tweet> getUserTweet(String pseudo) {
		List<Tweet> res = new LinkedList<>();
		Iterator<Tweet> it = tweets.iterator();
		while(it.hasNext()) {
			Tweet t = it.next();
			if(pseudo.equals(t.getAuthor()))
				res.add(t);
		}
		return res;
	}

	/**
	 * All users
	 * @return List of users's pseudo
	 */
	public List<String> getUsers() {
		List<String> res = new ArrayList<String>();
		for(User u : users.values()) {
			res.add(u.getPseudo());
		}
		return res;
	}
}
